package fons.navigator.views.quadrant;

import java.awt.Color;
import java.awt.Font;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.List;

import com.google.common.collect.Lists;

import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolo.nodes.PText;
import edu.umd.cs.piccolox.util.PFixedWidthStroke;
import fons.navigator.util.piccolo.PPaths;

public class AverageCross extends PNode {
	
	private boolean showRelationFactors = false;
	private class RelationStroke extends PPath {
		private static final long serialVersionUID = 1L;
		private final double xToY;
		private final double yToX;
		private final PText percent;
		private boolean switchAxes = false;
		private boolean percentage;
		
		public RelationStroke (double xToY) {
			this.xToY = xToY;
			this.yToX = 1/xToY;
			this.percent = new PText();
			this.showAsPercentage();
			this.percent.setFont(RELATION_FONT);
			
			this.addInputEventListener(new PBasicInputEventHandler() {
				@Override
				public void mouseEntered(PInputEvent event) {
					if (showRelationFactors && event.isRightMouseButton()) {
						Point2D pickedPoint = event.getPosition();
						percent.setOffset(visualSpiral.getCamera().viewToLocal(pickedPoint));
						if (percent.getParent() == null) {
							visualSpiral.getCamera().addChild(percent);
						}
					}
				}
			});
		}
		
		public void showAsPercentage() {
			final double factor = switchAxes ? yToX : xToY;
			this.percent.setText("Anteil: " + Math.round(10000*factor)/100.0 + "%");
			this.percentage = true;
		}
		
		public void showAsRatio () {
			final double factor = switchAxes ? yToX : xToY;
			this.percent.setText((switchAxes ? "Y:X = 100:" : "X:Y = 100:") + Math.round(1000*factor)/10.0);
			this.percentage = false;
		}

		public void switchRelation() {
			switchAxes = !switchAxes;
			if (percentage) showAsPercentage();
			else showAsRatio();
		}
	}
	
	private static final long serialVersionUID = 1L;
	
	private static final Stroke CROSS_STROKE = new PFixedWidthStroke(2);
	private static final Paint CROSS_PAINT = new Color(50, 50, 50, 200);
	private static final Paint REGRESSION_PAINT = new Color(220, 220, 140, 80);
	
	private static final Paint POSITIVE_PAINT = new Color(120, 250, 120, 60);
	private static final Paint NEGATIVE_PAINT = new Color(250, 120, 120, 60);
	
	private static final Stroke RELATION_STROKE =
		new PFixedWidthStroke(2.0f);/*, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL, 10.0f,
							   new float[] { 5.0f, 10.0f }, 1.0f);*/
	private static final Paint RELATION_PAINT = new Color(120, 120, 160, 80);
	private static double RELATION_STEP = 1.618;
	private static final Font RELATION_FONT = new Font("Arial",Font.BOLD, 11);
		
	private final VisualSpiral visualSpiral;
	private final PPath xCoord;
	private final PPath yCoord;
	private PPath regressionArea;
	
	private PPath lowerLeft;
	private PPath upperRight;
	private int relationStrokeNumber = 10;
	private List<RelationStroke> relationStrokes = Lists.newArrayList();
		
	public AverageCross(VisualSpiral visualSpiral) {
		this.visualSpiral = visualSpiral;

		final double minX = visualSpiral.getXMinProjection();
		final double spanX = visualSpiral.getXSpanProjection();
		final double minY = visualSpiral.getYMinProjection();
		final double spanY = visualSpiral.getYSpanProjection();
		
		final double maxX = minX + spanX;
		final double maxY = minY + spanY;
		final double avgX = visualSpiral.xToProjection(visualSpiral.getXAvgValue());
		final double avgY = visualSpiral.yToProjection(visualSpiral.getYAvgValue());
		
		xCoord = new PPath();
		xCoord.append(PPaths.createArrowShape(new Point2D.Double(minX-1000, avgY),
				new Point2D.Double(maxX+1000, avgY), 2, 45), false);
		xCoord.setStroke(CROSS_STROKE);
		xCoord.setStrokePaint(CROSS_PAINT);
		addChild(xCoord);
		
		yCoord = new PPath();
		yCoord.append(PPaths.createArrowShape(new Point2D.Double(avgX, minY-1000),
				new Point2D.Double(avgX, maxY+1000), 2, 45), false);
		yCoord.setStroke(CROSS_STROKE);
		yCoord.setStrokePaint(CROSS_PAINT);
		addChild(yCoord);
		
		final double regXb = visualSpiral.getRegXb();
		final double regYb = visualSpiral.getRegYb();
		
		if (!Double.isNaN(regXb) && !Double.isNaN(regYb)) {

			double min = visualSpiral.getXMinValue() - 4*visualSpiral.getXSpanValue();
			double max = visualSpiral.getXMaxValue() + 4*visualSpiral.getXSpanValue();
			
			regressionArea = new PPath();
			//xReg.append(new Line2D.Double(
			regressionArea.setPathToPolyline(
				new Point2D[] {
					new Point2D.Double(
					visualSpiral.xToProjection(min),
					visualSpiral.yToProjection(visualSpiral.getYAvgValue() + (min - visualSpiral.getXAvgValue()) * regXb)
					), new Point2D.Double( 
					visualSpiral.xToProjection(max),
					visualSpiral.yToProjection(visualSpiral.getYAvgValue() + (max - visualSpiral.getXAvgValue()) * regXb)
					
					), new Point2D.Double(
					visualSpiral.xToProjection(max),
					visualSpiral.yToProjection(visualSpiral.getYAvgValue() + (max - visualSpiral.getXAvgValue()) / regYb)
					), new Point2D.Double(
					visualSpiral.xToProjection(min),
					visualSpiral.yToProjection(visualSpiral.getYAvgValue() + (min - visualSpiral.getXAvgValue()) / regYb)
					)
			});

			regressionArea.setStroke(null);
			regressionArea.setPaint(REGRESSION_PAINT);
			addChild(regressionArea);
			
			
			lowerLeft = new PPath();
			lowerLeft.setPathToPolyline(
				new Point2D[] {
				new Point2D.Double(
					visualSpiral.xToProjection(min),
					visualSpiral.yToProjection(visualSpiral.getYAvgValue() + (min - visualSpiral.getXAvgValue()) * regXb)
				), new Point2D.Double(
					visualSpiral.xToProjection(visualSpiral.getXAvgValue()),
					visualSpiral.yToProjection(visualSpiral.getYAvgValue())
				), new Point2D.Double(
					visualSpiral.xToProjection(max),
					visualSpiral.yToProjection(visualSpiral.getYAvgValue() + (max - visualSpiral.getXAvgValue()) / regYb)
				), new Point2D.Double(
					visualSpiral.xToProjection(min),
					visualSpiral.getYMaxProjection() + 4*visualSpiral.getYSpanProjection()
				)
			});
			
			lowerLeft.setStroke(null);
			lowerLeft.setPaint(POSITIVE_PAINT);
		
			
			upperRight = new PPath();
			upperRight.setPathToPolyline(
				new Point2D[] {
				new Point2D.Double(
					visualSpiral.xToProjection(min),
					visualSpiral.yToProjection(visualSpiral.getYAvgValue() + (min - visualSpiral.getXAvgValue()) / regYb)
				), new Point2D.Double(
					visualSpiral.xToProjection(visualSpiral.getXAvgValue()),
					visualSpiral.yToProjection(visualSpiral.getYAvgValue())
				), new Point2D.Double(
					visualSpiral.xToProjection(max),
					visualSpiral.yToProjection(visualSpiral.getYAvgValue() + (max - visualSpiral.getXAvgValue()) * regXb)
				), new Point2D.Double(
					visualSpiral.xToProjection(max),
					visualSpiral.getYMinProjection()-4*visualSpiral.getYSpanProjection()
				)
			});
			
			upperRight.setStroke(null);
			upperRight.setPaint(NEGATIVE_PAINT);
			
			updateRelationLines();
		}
	}
		
	private void updateRelationLines() {
		this.removeChildren(this.relationStrokes);
		this.relationStrokes.clear();
		
		final double min = visualSpiral.xToProjection(0);
		final double max = visualSpiral.getXMaxProjection() + 3*visualSpiral.getXSpanProjection();
		
		double xToY = 1/16.0;
		for (int i = 0; i < relationStrokeNumber; ++i) {
			RelationStroke sameRelation = new RelationStroke(
					visualSpiral.yToValue(-max*xToY) / visualSpiral.xToValue(max)
					);
			sameRelation.setPathTo(new Line2D.Double(min, min*xToY, max, -max*xToY));
			sameRelation.setStroke(RELATION_STROKE);
			sameRelation.setStrokePaint(RELATION_PAINT);
			this.addChild(sameRelation);
			sameRelation.moveToBack();
			relationStrokes.add(sameRelation);
			xToY *= RELATION_STEP;
		}
		
		this.lowerLeft.moveToBack();
		this.upperRight.moveToBack();
		this.regressionArea.moveToBack();
	}

	public void highlightLowerLeft () {
		if (lowerLeft != null) {
			this.addChild(lowerLeft);
			lowerLeft.moveToBack();
		}
	}
	
	public void hideLowerLeft () {
		if (lowerLeft != null) 
			this.removeChild(lowerLeft);
	}
	
	public void highlightUpperRight () {
		if (upperRight != null) {
			this.addChild(upperRight);
			upperRight.moveToBack();
		}
	}
	
	public void hideUpperRight () {
		if (upperRight != null) 
			this.removeChild(upperRight);
	}
	
		
	private boolean colorSwitch = false;
	public void switchColors () {
		if (colorSwitch) {
			lowerLeft.setPaint(POSITIVE_PAINT);
			upperRight.setPaint(NEGATIVE_PAINT);
		} else {
			lowerLeft.setPaint(NEGATIVE_PAINT);
			upperRight.setPaint(POSITIVE_PAINT);
		}
		colorSwitch = !colorSwitch;
	}

	public void removeNodesFromCamera() {
		removeRelationFactors();
	}
	
	public void removeRelationFactors() {
		for (RelationStroke stroke: this.relationStrokes) {
			visualSpiral.getCamera().removeChild(stroke.percent);
		}
	}
	
	public void setRelationFactorsVisibility(boolean visible) {
		this.showRelationFactors = visible;
		if (!visible) removeRelationFactors();
	}
	
	public void showAsPercentage () {
		for (RelationStroke stroke: relationStrokes) {
			stroke.showAsPercentage();
		}
	}
	
	public void showAsRatio () {
		for (RelationStroke stroke: relationStrokes) {
			stroke.showAsRatio();
		}
	}

	public void switchRelation() {
		for (RelationStroke stroke: relationStrokes) {
			stroke.switchRelation();
		}
	}
}
